package com.boarsoft.boar.flow.biz;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.hibernate.query.Query;
import org.springframework.transaction.annotation.Transactional;

import com.boarsoft.boar.flow.FlowInstBiz;
import com.boarsoft.boar.flow.entity.FlowInst;
import com.boarsoft.common.Util;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.flow.core.SimpleFlow;
import com.boarsoft.flow.core.SimpleFlowImpl;
import com.boarsoft.flow.store.SimpleFlowPersister;
import com.boarsoft.hibernate.biz.SimpleBizImpl;
import com.boarsoft.serialize.TaggedJsonSerializer;

/**
 * 此类用于更新流程记录表<br>
 * 如果希望应用能直接更新流程记录表，则需手工定义相应的bean
 * 
 * @author Mac_J
 *
 */
public class SimpleFlowInstBizImpl extends SimpleBizImpl implements FlowInstBiz, SimpleFlowPersister {

	@Override
	@Transactional
	public List<SimpleFlow> list(int revMax1, int revMax2, int revMax3) {
		// 尝试修改状态并返回结果
		StringBuilder sb = new StringBuilder();
		sb.append("update ").append(FlowInst.class.getName());
		sb.append(" set revStatus = ").append(SimpleFlow.REV_EXECUTING);
		sb.append(" where id = :id and revStatus < ").append(SimpleFlow.REV_EXECUTING);
		Query<FlowInst> qry = dao.createQuery(FlowInst.class, sb.toString());
		List<SimpleFlow> rLt = new LinkedList<SimpleFlow>();

		// 优先处理超时且未冲正的正交易（无论是否批量冲正的情况）
		List<SimpleFlow> lt1 = this.list1(revMax1);
		for (SimpleFlow fo : lt1) {
			qry.setParameter("id", fo.getId());
			if (qry.executeUpdate() == 1) {
				rLt.add(fo);
			}
		}
		// 其次处理状态为失败且未冲正的正交易（只能是批量冲正的情况）
		List<SimpleFlow> lt2 = this.list2(revMax1);
		for (SimpleFlow fo : lt2) {
			qry.setParameter("id", fo.getId());
			if (qry.executeUpdate() == 1) {
				rLt.add(fo);
			}
		}
		// 最后处理冲正失败但未达到冲正次数上限的正交易
		List<SimpleFlow> lt3 = this.list3(revMax1);
		for (SimpleFlow fo : lt3) {
			qry.setParameter("id", fo.getId());
			if (qry.executeUpdate() == 1) {
				rLt.add(fo);
			}
		}
		return rLt;
	}

	@Override
	@Transactional
	public void save(SimpleFlow a) {
		// 不需要冲正的正交易，如：工作流，也需要保存
		FlowInst o = this.transfer(a);
		dao.save(o);
		// 如果此流程为反交易流程，更新对应的正交易状态为冲正中
		if (Util.strIsNotEmpty(a.getRevFor())) {
			FlowInst z = dao.get(FlowInst.class, a.getRevFor());
			z.setRevStatus(SimpleFlow.REV_EXECUTING);
			dao.update(z);
		}
	}

	@Override
	@Transactional(readOnly = true)
	public SimpleFlow load(String flowId, String exeId) {
		FlowInst a = dao.get(FlowInst.class, exeId);
		if (a == null) {
			return null;
		}
		return this.transfer(a);
	}

	@Override
	@Transactional
	public boolean update(SimpleFlow a) {
		FlowInst o = dao.get(FlowInst.class, a.getId());
		if (o == null) {
			return false;
		}
		// 只允许修改以下内容，注释掉的都是不应也不会变的属性
		try {
			o.setData(TaggedJsonSerializer.serialize(a.getData()));
		} catch (Exception e) {
			throw new UnsupportedOperationException(//
					String.format("Can not convert input parameter of flow %s", a.toString()));
		}
		// o.setDeadline(a.getDeadline());
		o.setEndTime(a.getEndTime());
		if (o.getTime() == 0L && o.getEndTime() > 0L) {
			o.setTime(o.getEndTime() - o.getStartTime());
		}
		// o.setFlowId(a.getFlowId());
		// o.setStartTime(a.getStartTime());
		o.setStatus(a.getStatus());
		o.setStep(a.getStep());
		// o.setTargetId(targetId);
		o.setRevEntry(a.getRevEntry()); // 更新反交易入口
		try {
			o.setError(TaggedJsonSerializer.serialize(a.getError()));
		} catch (Exception e) {
			throw new UnsupportedOperationException(//
					String.format("Can not convert throwable of flow %s", a.toString()));
		}
		dao.update(o);

		// 如果此流程为反交易流程，更新对应的正交易状态
		if (Util.strIsNotEmpty(a.getRevFor())) {
			FlowInst z = dao.get(FlowInst.class, a.getRevFor());
			z.setRevTime(z.getRevTime() + 1);
			switch (o.getStatus()) {
			case SimpleFlow.STATUS_SUCCESS:
				z.setRevStatus(SimpleFlow.REV_SUCCESS);
				break;
			case SimpleFlow.STATUS_FALED:
			case SimpleFlow.STATUS_TIMEOUT:
				if (z.getRevTime() >= z.getRevLimit()//
						|| z.getRevType() != SimpleFlow.REV_TYPE_BATCH) {
					z.setRevStatus(SimpleFlow.REV_FAILED);
				}
				break;
			}
			dao.update(z);
		}
		return true;
	}

	@Transactional
	protected void check() {
		String en = FlowInst.class.getName();
		StringBuilder sb = new StringBuilder();
		// 查询所有“超时”的交易，将状态改为超时
		sb.setLength(0);
		sb.append("update ").append(en);
		sb.append(" set status = ").append(SimpleFlow.STATUS_TIMEOUT);
		// NEW状态下的流程不会被持久化？REVERSING状态的也记录不应“被超时”
		sb.append(" where status = ").append(SimpleFlow.STATUS_EXECUTING);
		sb.append(" and deadline <= ").append(new Date().getTime());
		// sb.append(" where o.status in (").append(SimpleFlow.STATUS_NEW);
		// sb.append(", ").append(SimpleFlow.STATUS_EXECUTING);
		// sb.append(") and o.deadline <= ").append(new Date().getTime());
		dao.executeUpdate(sb.toString());

		// 查询所有状态为“冲正中”且达到冲正次数上限的正交易，检查是否还有“执行中”的反交易
		// 注：只可能因为出现这一种情况，因为已通过事务保证了反交易完成并更新其状态时一定会更新对应的正交易状态
		sb.setLength(0);
		sb.append("update ").append(en);
		sb.append(" o set o.revStatus = ").append(SimpleFlow.REV_FAILED);
		sb.append(" where o.revStatus = ").append(SimpleFlow.REV_EXECUTING);
		// 如果不存在正在执行或成功状态的反交易，且冲正次数达到上限，则表示当前正交易冲正失败
		sb.append(" and o.revTime >= o.revLimit and not exists (select a.id from ");
		sb.append(en).append(" a where a.revFor = o.id and a.status = ");
		sb.append(SimpleFlow.STATUS_EXECUTING).append(")");
		dao.executeUpdate(sb.toString());
	}

	@Transactional(readOnly = true)
	protected List<SimpleFlow> list1(int limit) {
		if (limit < 1) {
			return new ArrayList<SimpleFlow>();
		}
		// 查询超时未冲正的交易（无论是否批量冲正的情况）
		StringBuilder sb = new StringBuilder();
		sb.append("from ").append(FlowInst.class.getName());
		// 前面有通过检查deadline来检查交易是否超时，所以这里直接判断状态即可
		// 如果有执行冲正，并未完成冲正，这里状态一定是REVERSING，REVERSING状态的记录不会“被超时”
		sb.append(" where status = ").append(SimpleFlow.STATUS_TIMEOUT);
		// 必须是正交易，且有要求冲正但还未执行冲正，且配置了反交易
		sb.append(" and revType != ").append(SimpleFlow.REV_TYPE_IGNORE);
		sb.append(" and revStatus = ").append(SimpleFlow.REV_NONE);
		// 对于此类交易，按发生时间排序
		sb.append(" and revFor = '' and revCode != '' order by startTime");
		List<FlowInst> aLt = dao.list(sb.toString(), 0, limit);
		List<SimpleFlow> rLt = new ArrayList<SimpleFlow>(aLt.size());
		for (FlowInst a : aLt) {
			rLt.add(this.transfer(a));
		}
		return rLt;
	}

	@Transactional(readOnly = true)
	protected List<SimpleFlow> list2(int limit) {
		if (limit < 1) {
			return new ArrayList<SimpleFlow>();
		}
		// 查询失败未冲正的交易（只能是批量冲正的情况）
		StringBuilder sb = new StringBuilder();
		sb.append("from ").append(FlowInst.class.getName());
		// 此处必须是明确失败的交易，因为“未超时”交易只有失败状态才可以冲正
		sb.append(" where status = ").append(SimpleFlow.STATUS_FALED);
		// 非批量冲正的交易（同步或异步冲正），除非超时，不应由批量来调起
		sb.append(" and revType = ").append(SimpleFlow.REV_TYPE_BATCH);
		// 必须是正交易，且有要求冲正但还未执行冲正，且配置了反交易
		sb.append(" and revStatus = ").append(SimpleFlow.REV_NONE);
		// 对于此类交易，也按发生时间排序
		sb.append(" and revFor = '' and revCode != '' order by startTime");
		List<FlowInst> aLt = dao.list(sb.toString(), 0, limit);
		List<SimpleFlow> rLt = new ArrayList<SimpleFlow>(aLt.size());
		for (FlowInst a : aLt) {
			rLt.add(this.transfer(a));
		}
		return rLt;
	}

	@Transactional(readOnly = true)
	protected List<SimpleFlow> list3(int limit) {
		if (limit < 1) {
			return new ArrayList<SimpleFlow>();
		}
		// 最后处理冲正失败但未达到冲正次数上限的正交易
		StringBuilder sb = new StringBuilder();
		// 已通过事务保证反交易成功时，正交易状态一定也是是完成状态
		sb.append("from ").append(FlowInst.class.getName());
		// REV_EXECUTING状态已经能确定当前交易为正交易，且需要冲正并配置了反交易
		sb.append(" where revStatus = ").append(SimpleFlow.REV_EXECUTING);
		// 对于此类交易，按冲正次数和发生时间排序
		sb.append(" and revTime < revLimit order by revTime, startTime");
		List<FlowInst> aLt = dao.list(sb.toString(), 0, limit);
		List<SimpleFlow> rLt = new ArrayList<SimpleFlow>(aLt.size());
		for (FlowInst a : aLt) {
			rLt.add(this.transfer(a));
		}
		return rLt;
	}

	/**
	 * flow.property is b, entity property is a
	 * 
	 * @param a
	 * @return
	 */
	protected SimpleFlow transfer(FlowInst a) {
		SimpleFlow b = new SimpleFlowImpl(a.getFlowId(), a.getId());
		b.setId(a.getId());
		b.setName(a.getName());
		try {
			b.setData(TaggedJsonSerializer.deserialize1((String) a.getData()));
		} catch (Exception e) {
			throw new UnsupportedOperationException(String.format(//
					"Can not convert throwable of flow %s", a.toString()), e);
		}
		b.setDeadline(a.getDeadline());
		b.setEndTime(a.getEndTime());
		b.setStartTime(a.getStartTime());
		b.setStatus(a.getStatus());
		b.setStep(a.getStep());
		b.setRevType(a.getRevType());
		b.setRevCode(a.getRevCode());
		b.setRevEntry(a.getRevEntry());
		b.setRevFor(a.getRevFor());
		b.setRevLimit(a.getRevLimit());
		b.setRevTime(a.getRevTime());
		b.setRevStatus(a.getRevStatus());
		try {
			b.setRevError(TaggedJsonSerializer.deserialize1(//
					(String) a.getRevError(), Throwable.class));
		} catch (Exception e) {
			throw new UnsupportedOperationException(String.format(//
					"Can not convert revError of flow %s", a.toString()), e);
		}
		try {
			b.setError(TaggedJsonSerializer.deserialize1(//
					(String) a.getError(), Throwable.class));
		} catch (Exception e) {
			throw new UnsupportedOperationException(String.format(//
					"Can not convert error of flow %s", a.toString()), e);
		}
		b.setAddr(a.getAddr());
		return b;
	}

	/**
	 * flow.property is b, entity property is a
	 * 
	 * @param a
	 * @return
	 */
	protected FlowInst transfer(SimpleFlow b) {
		FlowInst a = new FlowInst();
		a.setId(b.getId());
		a.setName(b.getName());
		try {
			a.setData(TaggedJsonSerializer.serialize(b.getData()));
		} catch (Exception e) {
			throw new UnsupportedOperationException(String.format(//
					"Can't convert data of flow %s to entity field", b.toString()), e);
		}
		a.setDeadline(b.getDeadline());
		a.setEndTime(b.getEndTime());
		a.setFlowId(b.getFlowId());
		a.setStartTime(b.getStartTime());
		a.setStatus(b.getStatus());
		a.setStep(b.getStep());
		a.setRevType(b.getRevType());
		a.setRevCode(b.getRevCode());
		a.setRevEntry(b.getRevEntry());
		a.setRevFor(b.getRevFor());
		a.setRevLimit(b.getRevLimit());
		a.setRevTime(b.getRevTime());
		a.setRevStatus(b.getRevStatus());
		try {
			a.setRevError(TaggedJsonSerializer.serialize(b.getRevError()));
		} catch (Exception e) {
			throw new UnsupportedOperationException(String.format(//
					"Can't convert revError of flow %s to entity field", b.toString()), e);
		}
		try {
			a.setError(TaggedJsonSerializer.serialize(b.getError()));
		} catch (Exception e) {
			throw new UnsupportedOperationException(String.format(//
					"Can't convert error of flow %s to entity field", b.toString()), e);
		}
		a.setAddr(b.getAddr());
		return a;
	}

	@Override
	@Transactional
	public void delete(SimpleFlow flow) {
		dao.delete(FlowInst.class, flow.getId());
	}

	@Override
	@Transactional
	public SimpleFlow remove(String flowId, String exeId) {
		FlowInst o = dao.get(FlowInst.class, exeId);
		return this.transfer(o);
	}

	@Override
	@Transactional(readOnly = true)
	public PagedResult<SimpleFlow> list(String flowId, String key, int pageNo, int pageSize) {
		StringBuilder sb = new StringBuilder();
		sb.append("and flowId='").append(flowId).append("'");
		String wql = sb.toString();
		int total = dao.getTotal(FlowInst.class, wql);
		List<FlowInst> eLt = dao.list(FlowInst.class, wql, pageNo, pageSize);
		List<SimpleFlow> rLt = new ArrayList<SimpleFlow>(eLt.size());
		for (FlowInst a : eLt) {
			rLt.add(this.transfer(a));
		}
		return new PagedResult<SimpleFlow>(total, rLt, pageNo, pageSize);
	}

}
